package com.you.weixinpay.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.you.weixinpay.enums.WxNotifyType;
import com.you.weixinpay.properties.WeChatProperties;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;

/**
 * JSAPI支付工具类
 * 用于处理微信公众号支付、小程序支付等场景
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WeChatJsApiPayUtil {

    private final CloseableHttpClient httpClient;
    private final WeChatProperties weChatProperties;
    private final Verifier verifier;

    // JSAPI支付接口地址
    public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    // 退款接口地址
    public static final String REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";

    /**
     * 创建JSAPI支付订单
     * @param orderNo 商户订单号
     * @param total 金额(元)
     * @param description 商品描述
     * @param openid 用户openid
     * @return 包含预支付交易会话标识的JSON对象
     */
    public JSONObject createOrder(String orderNo, BigDecimal total, String description, String openid) throws Exception {
        JSONObject request = new JSONObject();
        request.put("appid", weChatProperties.getAppid());
        request.put("mchid", weChatProperties.getMchid());
        request.put("description", description);
        request.put("out_trade_no", orderNo);
        request.put("notify_url", weChatProperties.getNotifyUrl().concat(WxNotifyType.JSAPI_NOTIFY.getType()));

        // 设置支付者信息(openid)
        JSONObject payer = new JSONObject();
        payer.put("openid", openid);
        request.put("payer", payer);

        // 设置金额信息(转换为分)
        JSONObject amount = new JSONObject();
        amount.put("total", total.multiply(new BigDecimal(100)).intValue());
        amount.put("currency", "CNY");
        request.put("amount", amount);

        // 创建HTTP POST请求
        HttpPost httpPost = new HttpPost(JSAPI);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        // 执行请求并处理响应
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("微信JSAPI支付返回数据: {}", bodyAsString);
            log.info("微信JSAPI支付响应状态码: {}", statusCode);

            if (statusCode == 200) {
                return JSON.parseObject(bodyAsString);
            } else {
                log.error("创建JSAPI支付订单失败");
                throw new RuntimeException("创建JSAPI支付订单失败: " + bodyAsString);
            }
        }
    }

    /**
     * 关闭订单
     * @param orderNo 商户订单号
     * @throws Exception 关闭订单异常
     */
    public void closeOrder(String orderNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", orderNo);

        JSONObject request = new JSONObject();
        request.put("mchid", weChatProperties.getMchid());

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 204) {
                log.info("微信JSAPI关单成功，订单号：{}", orderNo);
            } else {
                String responseBody = EntityUtils.toString(response.getEntity());
                log.error("微信JSAPI关单失败，响应状态码：{}，响应内容：{}", statusCode, responseBody);
                throw new RuntimeException("微信JSAPI关单失败：" + responseBody);
            }
        }
    }

    /**
     * 申请退款
     * @param outTradeNo 商户订单号
     * @param outRefundNo 商户退款单号
     * @param total 订单金额(元)
     * @param refund 退款金额(元)
     * @param reason 退款原因
     * @return 退款结果
     */
    public JSONObject refund(String outTradeNo, String outRefundNo, BigDecimal total, BigDecimal refund, String reason) throws Exception {
        JSONObject request = new JSONObject();
        request.put("out_trade_no", outTradeNo);
        request.put("out_refund_no", outRefundNo);
        request.put("notify_url", weChatProperties.getRefundNotifyUrl().concat(WxNotifyType.REFUND_NOTIFY.getType()));

        JSONObject amount = new JSONObject();
        amount.put("refund", refund.multiply(new BigDecimal(100)).intValue());
        amount.put("total", total.multiply(new BigDecimal(100)).intValue());
        amount.put("currency", "CNY");
        request.put("amount", amount);

        if (reason != null && !reason.isEmpty()) {
            request.put("reason", reason);
        }

        HttpPost httpPost = new HttpPost(REFUND_URL);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.setEntity(new StringEntity(request.toJSONString(), StandardCharsets.UTF_8));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("微信JSAPI退款返回数据: {}", bodyAsString);
            log.info("微信JSAPI退款响应状态码: {}", statusCode);
            if (statusCode == 200) {
                return JSON.parseObject(bodyAsString);
            } else {
                throw new RuntimeException("JSAPI退款申请失败: " + bodyAsString);
            }
        }
    }

    /**
     * 查询支付订单状态
     * @param orderNo 商户订单号
     * @return 订单状态
     */
    public String queryOrder(String orderNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s",
                orderNo, weChatProperties.getMchid());

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() == 200) {
                JSONObject result = JSON.parseObject(responseBody);
                log.info("完整【JSAPI支付订单】查询结果: {}", result);
                String tradeState = result.getString("trade_state");
                log.info("微信JSAPI订单【状态】查询结果，订单号：{}，状态：{}", orderNo, tradeState);
                return tradeState;
            } else {
                log.error("查询JSAPI订单状态失败, 响应: {}", responseBody);
                throw new RuntimeException("查询JSAPI订单状态失败: " + responseBody);
            }
        }
    }

    /**
     * 查询退款状态
     * @param refundNo 商户退款单号
     * @return 退款状态
     */
    public String queryRefund(String refundNo) throws Exception {
        String url = String.format("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s", refundNo);

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            if (response.getStatusLine().getStatusCode() == 200) {
                JSONObject result = JSON.parseObject(responseBody);
                log.info("完整【JSAPI退款状态】查询结果: {}", result);
                log.info("【JSAPI退款状态】查询结果: {}", result.getString("status"));
                return result.getString("status");
            } else {
                log.error("查询JSAPI退款状态失败, 响应: {}", responseBody);
                throw new RuntimeException("查询JSAPI退款状态失败: " + responseBody);
            }
        }
    }

    /**
     * 验证签名
     * @param request HTTP请求
     * @param requestBody 请求体内容
     * @return 是否验证通过
     */
    public boolean verifySignature(HttpServletRequest request, String requestBody) {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String serial = request.getHeader("Wechatpay-Serial");

        try {
            String message = timestamp + "\n" + nonce + "\n" + requestBody + "\n";
            return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
        } catch (Exception e) {
            log.error("验证JSAPI支付签名异常", e);
            return false;
        }
    }

    /**
     * 解密数据
     * @param cipherText 密文
     * @param associatedData 附加数据
     * @param nonce 随机串
     * @return 解密后的数据
     * @throws GeneralSecurityException 解密异常
     */
    public String decryptData(String cipherText, String associatedData, String nonce) throws GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                cipherText
        );
    }

    /**
     * 处理通知数据
     * @param resource 资源对象
     * @return 解密后的数据
     * @throws GeneralSecurityException 解密异常
     */
    public String processResource(JSONObject resource) throws GeneralSecurityException {
        String cipherText = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonceStr = resource.getString("nonce");
        return decryptData(cipherText, associatedData, nonceStr);
    }

    /**
     * 构建成功响应
     * @return 成功响应JSON字符串
     */
    public static String buildSuccessResponse() {
        JSONObject response = new JSONObject();
        response.put("code", "SUCCESS");
        response.put("message", "成功");
        return response.toJSONString();
    }

    /**
     * 构建成失败响应
     * @return 失败响应JSON字符串
     */
    public static String buildFailureResponse() {
        JSONObject response = new JSONObject();
        response.put("code", "FAILURE");
        response.put("message", "失败");
        return response.toJSONString();
    }

    /**
     * 解析通知事件类型
     * @param requestBody 请求体
     * @return 事件类型
     */
    public static String parseEventType(String requestBody) {
        JSONObject result = JSON.parseObject(requestBody);
        return result.getString("event_type");
    }

    /**
     * 获取通知资源对象
     * @param requestBody 请求体
     * @return 资源对象
     */
    public static JSONObject getResource(String requestBody) {
        JSONObject result = JSON.parseObject(requestBody);
        return result.getJSONObject("resource");
    }
}